#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

struct private_data {
    int led_gpio;
    int key_gpio;
    int key_gpio_int;
    int major;
    char *name;
    struct timer_list timer;
    struct input_dev *input;
};

static struct private_data g_private_data = {
    .name = "rk3588-gpio-input",
};

static irqreturn_t rk3588_gpio_input_interrupt_callback(int irq, void *dev_id)
{
    struct private_data *d = (struct private_data*)dev_id;
    disable_irq_nosync(irq);    
    mod_timer(&d->timer, jiffies + msecs_to_jiffies(100));
    return IRQ_HANDLED;
}

static void rk3588_gpio_input_timer_callback(struct timer_list *timer)
{
    int key =  gpio_get_value(g_private_data.key_gpio);
    gpio_set_value(g_private_data.led_gpio, key);
    // input_report_key 最后一个参数，0表示松开，1表示按下，这里因为gpio选择拉高，不接GND的时候默认高电平，
    // 接了GND是低电平，所以要取反，表示接地的时候按下按键
    input_report_key(g_private_data.input, KEY_0, !key);
    input_sync(g_private_data.input);
    enable_irq(g_private_data.key_gpio_int);
}

static int rk3588_gpio_input_parse_deviceetree(struct platform_device *pdev)
{
    int ret;
    ret = of_gpio_named_count(pdev->dev.of_node, "gpios");
    if (ret < 0) {
        printk(KERN_ERR "[%s][%d]get gpio devicetree num failed\n", __func__, __LINE__);
        return ret;
    }
    if (ret != 2) {
        printk(KERN_ERR "[%s][%d]gpio num isn't 2\n", __func__, __LINE__);
        ret = -EINVAL;
        return ret;
    }

    ret = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
    if (ret < 0) {
        printk(KERN_ERR "[%s][%d]get led-gpio from devicetree failed.ret=%d\n", __func__, __LINE__, ret);
        return ret;
    }
    g_private_data.led_gpio = ret;

    ret = of_get_named_gpio(pdev->dev.of_node, "gpios", 1);
    if (ret < 0) {
        printk(KERN_ERR "[%s][%d]get key-gpio from devicetree failed.ret=%d\n", __func__, __LINE__, ret);
        return ret;
    }    
    g_private_data.key_gpio = ret;
    return 0;
}

static int rk3588_gpio_input_gpio_init(void)
{
    int ret;
    ret = gpio_request(g_private_data.led_gpio, "led-gpio");
    if (ret) {
        printk(KERN_ERR "[%s][%d]led gpio_request %d failed\n", __func__, __LINE__, g_private_data.led_gpio);
        return -1;        
    }

    ret = gpio_request(g_private_data.key_gpio, "key-gpio");
    if (ret) {
        printk(KERN_ERR "[%s][%d]key gpio_request %d failed\n", __func__, __LINE__, g_private_data.key_gpio);
        return -2;
    }

    ret = gpio_direction_output(g_private_data.led_gpio, 0);
    if (ret) {
        printk(KERN_ERR "[%s][%d]gpio_direction_output failed\n", __func__, __LINE__);
        return -3;
    }

    ret = gpio_direction_input(g_private_data.key_gpio);
    if (ret) {
        printk(KERN_ERR "[%s][%d]gpio_direction_output failed\n", __func__, __LINE__);
        return -3;
    }
    return 0;
}

static int rk3588_gpio_input_interrupt_init(void)
{
    int ret;
    g_private_data.key_gpio_int = gpio_to_irq(g_private_data.key_gpio);

    ret = request_irq(g_private_data.key_gpio_int, rk3588_gpio_input_interrupt_callback, 
        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, g_private_data.name, &g_private_data);
    if (ret) {
        printk(KERN_ERR "[%s][%d]request irq failed\n", __func__, __LINE__);
        return ret;
    }

    timer_setup(&g_private_data.timer, rk3588_gpio_input_timer_callback, 0);
    return 0;
}

static int rk3588_gpio_input_input_device_init(void)
{
    int ret;
    g_private_data.input = input_allocate_device();
    if (!g_private_data.input) {
        printk(KERN_ERR "[%s][%d]allocate input device failed\n", __func__, __LINE__);
        return -1;
    }
    __set_bit(EV_KEY, g_private_data.input->evbit); // 设置产生按键事件
    __set_bit(EV_REP, g_private_data.input->evbit); // 重复事件
    __set_bit(KEY_0, g_private_data.input->keybit); // 设置产生哪些按键
    
    ret = input_register_device(g_private_data.input);
    if (ret) {
        printk(KERN_ERR "[%s][%d]register input device failed.ret=%d\n", __func__, __LINE__, ret);
        return -2;
    }
    return 0;
}

static int rk3588_gpio_input_probe(struct platform_device *pdev)
{
    int ret;
    ret = rk3588_gpio_input_input_device_init();
    if (ret == -1) {
        ret = -EINVAL;
        goto ALLOCATE_INPUTDEV_FAILED;
    } else if (ret == -2) {
        ret = -EINVAL;
        input_free_device(g_private_data.input);
        goto REGISTER_INPUTDEV_FAILED;
    }

    ret = rk3588_gpio_input_parse_deviceetree(pdev);
    if (ret) {
        printk(KERN_ERR "[%s][%d]rk3588_gpio_input_parse_deviceetree failed.ret=%d\n", __func__, __LINE__, ret);
        goto OF_FAILED;
    }

    ret = rk3588_gpio_input_gpio_init();
    if (ret == -1) {
        ret = -EIO;
        goto GPIO_REQUST_LED_FAILED;
    } else if (ret == -2) {
        ret = -EIO;
        goto GPIO_REQUST_KEY_FAILED;
    } else if (ret == -3) { 
        ret = -EIO;
        goto GPIO_DIRECTION_FAILED;
    }

    ret = rk3588_gpio_input_interrupt_init();
    if (ret) {
        printk(KERN_ERR "[%s][%d]rk3588_gpio_input_interrupt_init failed.ret=%d\n", __func__, __LINE__, ret);
        goto GPIO_DIRECTION_FAILED;
    }
    
    return 0;
GPIO_DIRECTION_FAILED:
    gpio_free(g_private_data.key_gpio);
GPIO_REQUST_KEY_FAILED:
    gpio_free(g_private_data.led_gpio);
GPIO_REQUST_LED_FAILED:
OF_FAILED:
    input_unregister_device(g_private_data.input);
REGISTER_INPUTDEV_FAILED:
ALLOCATE_INPUTDEV_FAILED:
    return ret;
}

static int rk3588_gpio_input_remove(struct platform_device *pdev)
{
    del_timer(&g_private_data.timer);
    free_irq(g_private_data.key_gpio_int, &g_private_data);
    gpio_free(g_private_data.key_gpio);
    gpio_free(g_private_data.led_gpio);
    input_unregister_device(g_private_data.input);
    //这里不用调用这个函数，可能注册的完成就把这个结构体清除了
    //input_free_device(g_private_data.input);
    return 0;
}

static struct of_device_id match_table[] = {
    {.compatible = "rk3588-gpio-input,guoerba"},
};

static struct platform_driver rk3588_gpio_input_driver = {
    .probe = rk3588_gpio_input_probe,
    .remove = rk3588_gpio_input_remove,
    .driver = {
        .name = "rk3588-gpio-input-driver",
        .of_match_table = match_table,
    }
};

static int __init rk3588_gpio_input_init(void)
{
    if (platform_driver_register(&rk3588_gpio_input_driver)) {
        printk("[%s][%d]register platform driver failed\n", __func__, __LINE__);
        return -EBUSY;
    }
    return 0;
}

static void __exit rk3588_gpio_input_exit(void)
{
    platform_driver_unregister(&rk3588_gpio_input_driver);
}

module_init(rk3588_gpio_input_init);
module_exit(rk3588_gpio_input_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yujie Guo");
MODULE_DESCRIPTION("rk3588-gpio-input driver for Learning");



